ゆるふわシェル問題 その4
全部で8問
そんなに難しくないはず
だいたい1~3コマンドで解ける
使う技術
問題
Q1 環境変数PATHにセットされているディレクトリの一覧を縦に並べて出力してください
code:a1.sh
echo $PATH | tr : \\n #シェル芸 問題の意図
環境変数PATHを縦に並べて確認したくなることはちょいちょいあるので
解説
linuxの環境変数PATHにはディレクトリが:区切りで設定されている なので:を改行文字\nに置換してあげればよい
Q2 以下のテキストファイルaの数値に3桁きざみの , (カンマ)をいれて出力してください
code:q2.sh
cat << EOS | tee a
1000
10000000
EOS
code:a2.sh
rev a | sed -E 's/.{3}/&,/g' | rev
または
code:a2_2.sh
cat a | numfmt --grouping
問題の意図
numfmtコマンドを知ってるかなぁ、と思い
解説
sedでやってる方
revで一度逆順にする
前方から「任意の文字が3文字連続する」正規表現でマッチ マッチした正規表現をそのまま使いつつ、後ろにカンマを付与して置換
revで元に戻す
numfmtのほう
オプション渡すだけ
Q3 以下のファイル a b をカンマ区切りで横に並べて出力してください
code:q3.sh
seq 1 3 > a
seq 3 5 > b
echo "期待値は以下"
code:txt
1,3
2,4
3,5
code:a3.sh
seq 1 3 > a
seq 3 5 > b
paste a b -d ,
問題の意図
pasteコマンドの確認
解説
pasteは引数にファイルを受け取って横に並べて出力する
横に並べる時の区切り文字を指定するときは-dオプションを使う
Q4 以下のfor文の引数のうち、「山田太郎」さん(表記のゆらぎをふくむ)にマッチするように変数を定義して、変数を呼び出すようにしてください
code:a4.sh
regexp="ここで定義"
for word in 山田太郎 山本太郎 山田一郎 山田たろう やまだ太郎; do
if "\$word" =~ {ここで使用} ; then
echo \$word
fi
done
code:a4.sh
regexp="(山田|やまだ)(太郎|たろう)"
for word in 山田太郎 山本太郎 山田一郎 山田たろう やまだ太郎; do
if "$word" =~ $regexp ; then
echo $word
fi
done
問題の意図
[[ ]]における正規表現の使い方を問う問題
結構これ間違えてしまう
解説
[[ 左 =~ 右 ]]と書く時、「右」に正規表現文字列を書くと正規表現と一致するか判定してくれる
この時、「左」はダブルクオートでくくっても良いが、「右」はダブルクオートでくくってはいけない
ダブルクオートも正規表現の判定に使われてしまうため
これは変数を展開する時も同じで、「左」はダブルクオートでくくっているが、「右」は変数をダブルクオートでくくってはいけない
例えば、以下は A しか出力されない
code:sh
山田 =~ ^山田$ && echo A
山田 =~ "^山田$" && echo B
Q5 1111年11月11日はいまから何日前?
code:a5.sh
seq -f "date -d '%g years ago' +%%Y" 1000 | sh | nl | grep 1110-9 | awk '{for (i=0; i<365; i++) { print ($1-1) * 365 + i }}' | sed -E "s/.*/date -d '& days ago' +'& %Y-%m-%d'/g" | sh | grep -F "1111-11-11" 問題の意図
dateの使い方の確認
これ今まで出した問題の中で一番難しい気がする
てかゆるふわな問題では無かったかもしれない
解説
seq -f "date -d '%g years ago' +%%Y" 1000ではdateコマンドの文字列を作っている
こういう文字列が返ってくる
date -d '1 years ago' +%Y
shにパイプで渡してコマンドを実行
実行結果にnlで連番を付与
1111年前後の年だけほしいのでgrepで絞り込み
awkで年から日数を生成
生成した日数の数値からsedでdateのコマンド文字列を生成
shで実行
実行結果のうち、1111-11-11をgrepすることで、その日付が今から何日前かがわかる
他
code:ps1
(Get-Date)-(Get-Date "1111/11/11") | Select-Object Days
Q6 bashのビルトイン関数ではないechoコマンドで寿司と出力してください
code:a6.sh
/bin/echo 寿司
問題の意図
なんとなく
普段使ってるechoはビルトイン(組み込み)関数なんだよということだけ
解説
単純にechoと入力するとビルトイン関数が呼ばれてしまうので、フルパスでechoを指定してあげればよい
Q7 以下のように引数を渡したときに、期待値の通り標準出力に出力できるように関数を定義してください
code:q7.sh
func1() {
: TODO
}
func1 "基本設計書 (1).xlsx" "" "world"
期待値
code:txt
a = 基本設計書 (1).xlsx
b =
c = world
code:a7.sh
func1() {
echo "a = $1"
echo "b = $2"
echo "c = $3"
}
func1 "基本設計書 (1).xlsx" "" "world"
問題の意図
関数の位置引数(positional parameter)の使い方とか
本当はダブルクオートでくくらなかったら位置がずれる、って問題にしたかったけれど問題の内容を間違えてしまった
解説
第一引数は$1に設定される
第二、第三も同様
Q8 何らかの方法で以下の順序でechoコマンドを定義するけれど、出力順序は B A となるようにしてください。echoの前後になにかしても良いです
code:q8.sh
echo A
echo B
code:a8.sh
(sleep 2; echo A) &
n=$!
(sleep 1; echo B) &
m=$!
wait $n
問題の意図
コマンドのバックグラウンド実行方法を問う
問題の説明の書き方が良くなかったなぁと反省
解説
コマンドの末尾に&を設定すると、そのコマンド呼び出しをバックグラウンドジョブとして実行してくれる
バックグラウンド実行されてるジョブを確認したいときはjobsと実行すると確認できる
複数のコマンドをまとめてバックグラウンドジョブに登録したいときは()でくくってサブシェルにする
サブシェルの最初にsleepを挟むことでechoが実行されるタイミングをずらすことで実行順序を入れ替えた
ただしこのままだとechoが出力される前に親のシェルプロセスが終了してしまうのでwaitを挟む
waitでは2つのバックグラウンドジョブを待つ必要があるので、バックグラウンドジョブ登録直後にプロセスIDを変数に設定する
$!では直前のバックグラウンドジョブのプロセスIDが格納される
「直前の」なので複数バックグラウンドジョブを登録する場合は、1つバックグラウンドジョブを登録するたびに何らかの別変数に$!の値を格納しないと上書きされる
最後にwaitで2つのジョブの完了を待機すれば完了
以上
余談
個人的にはQ5が一番難しいかな、と